/**DateTime utils for datetime converter or datetime arithmetic operation
 * by fuzhou huilin corp.ltd @author lms 2016.07.14
 */
package com.restphp.java.utils.date;

import com.restphp.java.utils.string.core.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class DateTimeUtils {
	private static Logger logger = LogManager.getLogger(DateTimeUtils.class);
	/**
	 * convert date to string with format,the format like these:
	  @param dt date of to be formated
	  @param format string,such as: "yyyy-MM-dd HH:mm:ss"
	  @return: success return string datetime, otherwise return null
	*/
	public static String formatDateTime(Date dt, String format){
		if (dt==null) {
			return null;
		}

		String str = null;
		try{
			DateFormat fmt = new SimpleDateFormat(format);
			str = fmt.format(dt);
			if (str.compareTo("1899-12-30")<=0 && format.compareTo("yyyy")>0) {
				str = null;
			}
		}catch(Exception e){
			logger.error(e, e);
		}
		return str;
	}

	/**
	 * format datetime to yyyy-MM-dd
	* @param date to format date
	* @return null when exception throw or return date string
	 */
	public static String dateToDateString(Date date) {
		return formatDateTime(date,"yyyy-MM-dd");
	}
	
	/**
	 * format datetime to HH:mm:ss
	* @param date to format date
	* @return null when exception throw or return time string
	 */
	public static String dateToTimeString(Date date) {
		return formatDateTime(date,"HH:mm:ss");
	}

	/**
	 * format datetime to yyyy-MM-dd HH:mm:ss
	* @param date to format date
	* @return null when exception throw or return time string
	 */
	public static String dateToDateTimeString(Date date) {
		return formatDateTime(date,"yyyy-MM-dd HH:mm:ss");
	}

	/**
	 * convert Date to long value
	 * @param date
	 * @return yyyyMMddHHmmss long value
	 */
	public static long datetimeToInt(Date date){
		String value = formatDateTime(date,"yyyyMMddHHmmss");
		return Long.parseLong(value);
	}

	/**
	 * convert Date to long value
	 * @param date
	 * @return unix time
	 */
	public static long datetimeToUnixTime(Date date){
		if (date==null) {
			return 0;
		}

		return date.getTime()/1000;
	}

	/**
	 * convert unixTime to Date
	 * @param unix time
	 * @return date
	 */
	public static Date datetimeToUnixTime(long unix){
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String d = format.format(unix);  
		Date ret;
		try {
			ret = format.parse(d);
			return ret;
		} catch (ParseException e) {
			logger.error(e, e);
		}
		return null;
	}
	
	/**
	 * convert yyyyMMdd date to yyyy-MM-dd date
	 * @param iDate format of yyyyMMdd integer value
	 * @return null if invalid nDate, or return yyyy-MM-dd string
	 */
	public static String formatNumericDate(int iDate){
		String tmp = Integer.toString(iDate);
		String ret = null;
		if (tmp.length()==8){
			ret = tmp.substring(0, 4)+"-"+tmp.substring(4,6)+"-"+tmp.substring(6,8);
		}
		return ret;
	}
	

	/**
	 * convert yyMMddHHNNSS date to yyyy-MM-dd HH:NN:SS date
	 * @param sDate format of yyyyMMdd integer value
	 * @return null if invalid nDate, or return yyyy-MM-dd HH:NN:SS string
	 */
	public static String formatNumericDatetime(String sDate){
		String ret = null;
		if (sDate.length()==12){
			ret = "20"+sDate.substring(0, 2)+"-"+sDate.substring(2,4)+"-"+sDate.substring(4,6)+" "+
				  sDate.substring(6,8)+":"+sDate.substring(8,10)+":"+sDate.substring(10,12);
		}
		return ret;
	}

	/**
	 * to inc time by indicating field with amount value
	 * @param dt Date to add time
	 * @param field to indicate which part to increase
	 * @param amount date's field to add amount
	 * @return new Date if successful, otherwise return null
	 */
	private static Date incTime(Date dt, int field, int amount){
		if (dt==null) {
			return null;
		}

		try{
			Calendar cd = Calendar.getInstance();
			cd.setTime(dt);
			cd.add(field, amount);
			return cd.getTime();
		}catch(Exception e){
			logger.error(e, e);
		}
		return null;
	}

	/**
	 * to increse years to Date
	 * @param dt Date to increse year
	 * @param years number of year to increase
	 * @return incresed Date if successful, otherwise return null
	 */
	public static Date incYear(Date dt, int years){
		return incTime(dt,Calendar.YEAR, years);
	}
	
	/**
	 * to increse months to Date
	 * @param dt Date to increse month
	 * @param months number of month to increase
	 * @return incresed Date if successful, otherwise return null
	 */
	public static Date incMonth(Date dt, int months){
		return incTime(dt,Calendar.MONTH, months);
	}
	
	/**
	 * to increse days to Date
	 * @param dt Date to increse day
	 * @param days number of day to increase
	 * @return incresed Date if successful, otherwise return null
	 */
	public static Date incDay(Date dt, int days){
		return incTime(dt,Calendar.DATE, days);
	}

	/**
	 * to increse hours to Date
	 * @param dt Date to increse hour
	 * @param hours number of hours to increase
	 * @return incresed Date if successful, otherwise return null
	 */
	public static Date incHour(Date dt, int hours){
		return incTime(dt,Calendar.HOUR, hours);
	}

	/**
	 * to increse minutes to Date
	 * @param dt Date to increse minute
	 * @param minutes number of minutes to increase
	 * @return incresed Date if successful, otherwise return null
	 */
	public static Date incMinute(Date dt, int minutes){
		return incTime(dt,Calendar.MINUTE, minutes);
	}

	/**
	 * to increse seconds to Date
	 * @param dt Date to increse second
	 * @param seconds number of seconds to increase
	 * @return incresed Date if successful, otherwise return null
	 */
	public static Date incSecond(Date dt, int seconds){
		return incTime(dt,Calendar.SECOND, seconds);
	}

	/**
	 * to calculate seconds between now and then
	 * @param now
	 * @param then
	 * @return seconds between now and then
	 */
	public static int secondsBetween(Date now, Date then){
        return (int) Math.abs(((now.getTime()-then.getTime())/1000));
	}
	
	/**
	 * to calculate minutes between now and then
	 * @param now
	 * @param then
	 * @return
	 */
	public static int minutesBetween(Date now, Date then){
		return secondsBetween(now, then)/60;
	}
	
	public static int daysBetween(Date now, Date then){
		return minutesBetween(now, then)/(60*24);
	}

	/**
	 * string to datetime
	 * @param str it's format is that: "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-ddTHH:mm:ss"
	 * @return
	 */
	public static Date strToDateTime(String str){
		if (StringUtils.isBlank(str)) return null;
		str = str.replace("T", " ");
		if (str.length()==16) str += ":00";
		return strToDateTime(str,"yyyy-MM-dd HH:mm:ss");
	}
	
	/**
	 * string to datetime
	 * @param str 
	 * @return
	 */
	public static Date strToDateTime(String str,String fmt){
		
		SimpleDateFormat format = new SimpleDateFormat(fmt);
		Date date = null;
		try{
			date = format.parse(str);
		}catch(ParseException e){
			logger.error(e, e);
		}
		return date;
	}
	
	/**
	 * string to date
	 * @param str it's format is that: "yyyy-MM-dd"
	 * @return
	 */
	public static Date strToDate(String str){
		if (StringUtils.isBlank(str)) return null;
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date date = null;
		try{
			date = format.parse(str);
		}catch(ParseException e){
			logger.error(e, e);
		}
		return date;
	}
	
	/**
	 * to get first day of indicate date
	 * @param date
	 * @return
	 * by @author lms 2017.03.08
	 */
	public static Date startOfTheMonth(Date date){
		Calendar cd = Calendar.getInstance();
		cd.setTime(date);
		cd.set(Calendar.DATE, 1);
		return cd.getTime();
	}


	/**
	 * to get last day of indicate date
	 * @param date
	 * @return
	 * by @author lms 2017.03.08
	 */
	public static Date endOfTheMonth(Date date){
		Calendar cd = Calendar.getInstance();
		cd.setTime(date);
		cd.set(Calendar.DATE, 1);
		cd.roll(Calendar.DATE, -1);
		return cd.getTime();
	}

	/**
	 * to truncate Date's time, such as "2017-3-8 15:45:12"=>"2017-3-8 00:00:00"
	 * @param date
	 * @return
	 * by @author lms 2017.03.08
	 */
	public static Date truncateTime(Date date){
		Calendar cd = Calendar.getInstance();
		cd.setTime(date);
		cd.set(Calendar.HOUR_OF_DAY, 0);
		cd.set(Calendar.MINUTE, 0);
		cd.set(Calendar.SECOND, 0);
		cd.set(Calendar.MILLISECOND, 0);
		
		return cd.getTime();	
	}

	/**
	 * to get the month days of indicating date
	 * @param date
	 * @return
	 * by @author lms 2017.05.25
	 */
	public static int daysOfTheMonth(Date date){
		Calendar cd = Calendar.getInstance();
		cd.setTime(date);
		cd.set(Calendar.DATE, 1);
		cd.roll(Calendar.DATE, -1);
		return cd.get(Calendar.DATE);
	}

	/**
	 * to get the day of indicating date
	 * @param date
	 * @return
	 * by @author lms 2017.05.25
	 */
	public static int dayOf(Date date){
		Calendar cd = Calendar.getInstance();
		cd.setTime(date);
		return cd.get(Calendar.DATE);
	}

	/**
	 * to get month of indicating date
	 * @param date
	 * @return 1~12
	 * by @author lms 2017.05.25
	 */
	public static int monthOf(Date date){
		Calendar cd = Calendar.getInstance();
		cd.setTime(date);
		return cd.get(Calendar.MONTH)+1;
	}

	/**
	 * to get year of indicating date
	 * @param date
	 * @return
	 * by @author lms 2017.05.25
	 */
	public static int yearOf(Date date){
		Calendar cd = Calendar.getInstance();
		cd.setTime(date);
		return cd.get(Calendar.YEAR);
	}
	
	//n天前的初始时间
	public static long getDayAgoFirstTime(int n) {		
		Calendar cal = Calendar.getInstance(); 
		cal.setTime(new Date());
		cal.add(Calendar.DATE, 0 - n);		
		cal.set(Calendar.HOUR_OF_DAY,0);
		cal.set(Calendar.MINUTE,0);
		cal.set(Calendar.SECOND,0);
		cal.set(Calendar.MILLISECOND,0);
		return cal.getTimeInMillis();
	}
	
	//n个月的初始时间
	public static long getMonAgoFirstTime(int n) {		
		Calendar cal = Calendar.getInstance(); 
		cal.setTime(new Date());
		if(n > 0) {
			cal.add(Calendar.MONTH,0 - n);
		}
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.HOUR_OF_DAY,0);
		cal.set(Calendar.MINUTE,0);
		cal.set(Calendar.SECOND,0);
		cal.set(Calendar.MILLISECOND,0);
		return cal.getTime().getTime();
	}
	
	//n分钟前的初始时间
	public static long getMinAgoFirstTime(int n) {		
		Calendar cal = Calendar.getInstance(); 
		cal.setTime(new Date());
		if(n > 0) {
			cal.add(Calendar.MINUTE,0 - n);
		}
		cal.set(Calendar.SECOND,0);
		cal.set(Calendar.MILLISECOND,0);
		return cal.getTime().getTime();
	}
	
	/**
	 * 获取分钟戳 如201904201427
	 * @param date
	 * @return
	 */
	public static long getMinuteStamp(Date date) {
		String minStr = DateTimeUtils.formatDateTime(date, "yyyyMMddHHmm");
		return Long.parseLong(minStr);
	}
	
	/**
	 * 获取小时戳 如2019042014
	 * @param date
	 * @return
	 */
	public static int getHourStamp(Date date) {
		String hourStr = DateTimeUtils.formatDateTime(date, "yyyyMMddHH");
		return Integer.parseInt(hourStr);
	}
	
	/**
	 * 获取日期戳 如20190420
	 * @param date
	 * @return
	 */
	public static int getDayStamp(Date date) {
		String hourStr = DateTimeUtils.formatDateTime(date, "yyyyMMdd");
		return Integer.parseInt(hourStr);
	}
	
	/**
	 * 获取月份戳 如201904
	 * @param date
	 * @return
	 */
	public static int getMonthStamp(Date date) {
		String hourStr = DateTimeUtils.formatDateTime(date, "yyyyMM");
		return Integer.parseInt(hourStr);
	}
	
	public static void main(String args[]){
		String str = "2018-06-18 18:00";
		System.out.println(DateTimeUtils.strToDateTime(str));
	}
}
